/*
 * kernel.c
 *
 * Created: 5/21/2011 5:14:20 PM
 *  Author: X-Ravin
 */ 
#include "kernel.h"

struct proc_block process[NUM_PROCS + 1];
char isr[19];

//TODO: Prog block needs to be non-volatile eventually.
struct program_block program[NUM_PROCS];

int main(void)
{
	
	char c, i;
	
	current_proc = 0;
	isr[USART1_owner] = 0;
	
	UCSR1A=0x00;
    UCSR1B=0x18;
    UCSR1C=0x06;
    UBRR1H=0x00;
    UBRR1L=0x08;
	UCSR0A=0x00;
    UCSR0B=0x18;
    UCSR0C=0x06;
    UBRR0H=0x00;
    UBRR0L=0x08;
	TCCR0B |= 0x03;
	TIMSK0 |= 1;
	//UCSR1B |= 0x80;
	sei();
	
	//Init kernel process block
	program[0].offset_address = FREE_FLASH_START;
	process[0].state = RUNNING;
	process[0].heap_start_address = 0x100;

	//Test data 
	//TODO Change to being right after kernel when implemented

	program[0].offset_address = 0xF00;
	program[0].state = RUNNABLE;
	process[1].state = QUEUED;
	process[1].stack_pointer = 0x5FA;
	process[1].program = 0;
	process[1].heap_start_address = 0x400;

	//program[2].offset_address = 0x1544;
	
	//writeFlashProgram(2, 150);

	
	//TODO change to i = 1
	for(i = 2; i < NUM_PROCS; i++)
	{
		program[i].offset_address = FLASHEND - 512;
		process[i].heap_start_address = RAMEND - 0x100;
	}
	
	puts_f(0);

	while(1)
	{
		c = getchar();

		switch(c)
		{
			case 'i':
				installProgram();
				break;
			case 'r':
				puts_f(1);
				c = getchar();
				runProgram(c - 48);
				puts_f(2);
				putchar(c);
				putchar('\n');
				break;
			case 'l':
				list();
				break;
		}

	}
	return 0;
}


void list(void)
{
	char i;
	char temp[6];
	
	puts_f(3);
	puts_f(4);
	for(i = 0; i < NUM_PROCS; i++)
	{
		if(program[i].state == FREE) continue;	
		putchar(program[i].PID + 48);
		putchar('\t');
		if(program[i].state == RUNNABLE)
		{
			itoa(process[program[i].PID].size,temp);
			puts(temp);
			putchar('\t');
			putchar('\t');
			itoa(program[i].size,temp);
			puts(temp);
		}
		else
		{
			itoa(0,temp);
			puts(temp);
			putchar('\t');
			putchar('\t');
			itoa(0,temp);
			puts(temp);
		}

		putchar('\t');
		putchar('\t');
		if(program[i].state == FREE)
			puts_f(7);
		else
		{
			if(process[program[i].PID].state == RUNNING)
				puts_f(8);
			else if(process[program[i].PID].state == RUNNABLE)
				puts_f(9);
			else if(process[program[i].PID].state == FINISHED)
				puts_f(10);
		}
		putchar('\n');
	}
	putchar('\n');
}

void runProgram(char prog)
{
	if(program[prog].state == RUNNABLE)
		process[program[prog].PID].state = QUEUED;
	else
	{
		putchar(prog + 48);
		puts_f(15);
	}
}

void installProgram(void)
{
	//flashSize = .text + .data
	//ramSize = .data + .bss + stack_estimation
	
	cli(); //No interruptions while installing, full kernel control

	unsigned int flashSize;
	unsigned int ramSize;
	char data[2];
	char prog;

	puts_f(5);

	data[1] = getchar();
	data[0] = getchar();
	flashSize = ((int)data[1] << 8) + (int)data[0];
	data[1] = getchar();
	data[0] = getchar();
	//ramSize = ((int)data[1] << 8) + (int)data[0];
	ramSize = data[0];

	//Allocate space, or quit if that fails
	prog = preallocProgram(flashSize, ramSize);
	if(prog == ERROR) return;

	//TODO: Send back info
	
	//With space ready to go, burn it in
	writeFlashProgram(prog, flashSize);

	puts_f(6);

	sei();
}
